//+------------------------------------------------------------------+
//|                                              FillForexSymbol.mq5 |
//|                                  Copyright 2024, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2024, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"

#include "../../Include/MyInclude/WavePoint.mqh"
#include "../../Include/MyInclude/CommonLog.mqh"
#include <MyInclude/Swapper.mqh>

/*
#include <ThirdParty/log4mqllib.mqh>
sinput ENUM_LOG_LEVEL       InpLogLevel       = LOGLEVEL_OFF;             // Loglevel
sinput ENUM_LOG_MODE        InpLogMode        = LOG_MODE_CONSOLE;  // Logmode
sinput string               InpLogPattern     = "%-5level %method - %msg"; // Message pattern
sinput string               InpLogFileSpec    = "%prgname";                // Logfile name
sinput ENUM_LOGFILE_TXTMODE InpLogFileTxtMode = LOGFILEMODE_ANSI;          // Logfile text mode
sinput ENUM_LOG_ROLLMODE    InpLogFileRoll    = LOG_ROLLMODE_DAILY;        // Logfile roll policy
sinput string               InpRollFileSpec   = "%prgname-%02i";           // File roll pattern
CLogManager   LogManager(Logger,InpLogLevel,InpLogMode,InpLogPattern,InpLogFileSpec,InpLogFileTxtMode,InpLogFileRoll,InpRollFileSpec);
*/

const int SHOW_CREDIT_MIN = 75; // 显示分数的下限

const uchar i_data_month = 24;  // 在多少月份内考查最值

const ulong i_magic_number = 1;

const ENUM_TIMEFRAMES i_avg_amp_timeframe = PERIOD_D1; // 计算平均振幅的时间周期
const int i_avg_amp_periods = 10; // 计算10个周期的平均振幅
//-- MarketWatch 相关--

// 是否整理MW列表
const bool i_mw_tidy_flag = false; 

// 是否只取MW列表中的货币对
bool i_get_mw_only = false; 

// 作用于外汇货币对
const bool i_forex_only = true; 

// 允许存在的最大点差
// const uchar i_mw_max_spread = 75; 

// 选取货币对时允许的最小价差比(平均振幅/点差))
const int i_min_price_spread = 9;

datetime MIN_BEGIN_DATE = D'2000-01-01';

// 日志输出类对象, 使用之前需要设置输出级别
CommonLog m_log = NULL;
// 日志输出级别
const CommonLog::enumLogLevel i_log_level = CommonLog::INFO;
// const CommonLog::enumLogLevel i_log_level = CommonLog::DEBUG;
//+------------------------------------------------------------------+
void OnStart()
{ 
  
  m_log = new CommonLog(i_log_level);
  m_log.info("****Start****");

  datetime begin_date, end_date;
  initMW();
  getDate(begin_date, end_date); 
  string symbol = "";
  // symbol = "XAGUSD";
  fruitForex(symbol, begin_date, end_date); 
  m_log.info("****End****");
}

void initDateScope()
{
  
}

void initForex(datetime &begin_date, datetime &end_date)
{  
  // bool is_custom;
  // if ("" != symbol && !SymbolExist(symbol, is_custom))
  // {
  //   // 如果输入了错误的货币对, 则由系统分配当前的货币对名称
  //   symbol = Symbol();
  // }
  getDate(begin_date, end_date);
}

/**
 * 根据输入的考察范围(月)确定起始日期和结束日期
 */
void getDate(datetime &begin_date, datetime &end_date)
{
  string symbol = "";
  symbol = Symbol();
  end_date = TimeCurrent();
  int bars = iBars(symbol, PERIOD_MN1);
  if (bars >= i_data_month)
  {
    bars = i_data_month;
  }
  
  begin_date = iTime(symbol, PERIOD_MN1, bars);

}
/**
 * 遍历平台货币对判断是否符合交易条件
 */
bool fruitForex(string symbol, datetime begin_date, datetime end_date)
{
  if (begin_date <= MIN_BEGIN_DATE)
  {
    // printf("开始日期'%s'不符合要求", TimeToString(begin_date));
    m_log.error("开始日期'" + TimeToString(begin_date) + "'不符合要求" );
    return false;
  }
  int mt_total = SymbolsTotal(i_get_mw_only); // true: from mw
  // printf("平台支持的所有货币对数量: %d", mt_total);
  // m_log.info("平台支持的所有货币对数量: " + mt_total);
  long spread = 0;

  string fix_float = "";
  // 是否选取为有效货币对
  // bool valid_flag = false;

  if (symbol != "")
  {
    printLevel(symbol, begin_date, end_date);
    return true;
  }
  
  string symbols[];
  filterSymbols(i_forex_only, i_min_price_spread, symbols);
  int symbol_count = ArraySize(symbols);
  for (int i=0; i<symbol_count; ++i)
  {
    printCredits(symbols[i], begin_date, end_date);
  }
  return true;
}

/**
 * 在 MarketWatch 中挑选交易对象筛选符合条件的交易对象数组 
 * @param only_forex 是否只挑选外汇交易对象
 * @param min_price_spread 最小价差比
 * @param symbols 输出参数, 
 */
void filterSymbols(bool only_forex, int min_price_spread, string &symbols[])
{
  bool in_mw = true;
  int total = SymbolsTotal(in_mw); // true: from mw
  string symbol = "";
  int spread = 0;
  int symbols_long = 0;
  for (int i=0; i<total; ++i)
  {
    symbol = SymbolName(i, in_mw); // true: from 列表中取一个symbol
    if (SymbolInfoInteger(symbol, SYMBOL_TRADE_MODE) == SYMBOL_TRADE_MODE_DISABLED)
    {
      m_log.debug(symbol + " 不能正常交易, 跳过");
      continue;
    }
    // spread = SymbolInfoInteger(symbol, SYMBOL_SPREAD);
    
    if (only_forex)
    {
      if (isForexSymbol(symbol))
      { // 是外汇货币对
        spread = validSpread(symbol);
        // bool is_spread_float=SymbolInfoInteger(symbol, SYMBOL_SPREAD_FLOAT);
        if (0 == spread)
        {
        //   m_log.info(StringFormat("%s %s spread:%d", symbol,(is_spread_float ? "float" : "fix"), spread));  
          continue;
        }
      } else {
        m_log.debug(symbol + "不是外汇货币对, 跳过");
        continue;
      }
    }
    double point = SymbolInfoDouble(symbol,SYMBOL_POINT);
    double avg_amp_litude = calculateAverageAmplitude(symbol, i_avg_amp_timeframe,i_avg_amp_periods);
    double price_spread = avg_amp_litude/(spread * point);
    // double price_spread = iClose(symbol, PERIOD_CURRENT, 0)/(spread * point);
    
    // if (avgAmplitude >= 0)
    // {
    //     Print("Average amplitude for ", symbol, " (", EnumToString(timeframe), ") over ", periods, " periods is: ", avgAmplitude);
    // }
    if (price_spread < min_price_spread)
    {
      m_log.info(symbol + " 价差比(" + (int)round(price_spread) + ")小于指定值, 跳过");
      // printf("%s 为%s点差(%d)超出指定值, 跳过", symbol, fix_float, spread);
      continue;
    }

    ArrayResize(symbols, ++symbols_long);
    symbols[symbols_long -1] = symbol;
  }
}

/**
 * 计算指定货币对在指定周期和周期个数情况下的价格平均振幅
 * 成功返回值, 失败返回-1
 * @param symbol 货币对
 * @param timeframe 周期
 * @param periods 周期个数
 */
double calculateAverageAmplitude(string symbol, ENUM_TIMEFRAMES timeframe, int periods)
{
    // 定义数组存储最高价和最低价
    double highs[];
    double lows[];
    
    // 复制最高价数据
    if (CopyHigh(symbol, timeframe, 0, periods, highs) < periods)
    {
        Print("Failed to copy high prices for ", symbol);
        return -1; // 返回错误值
    }
    
    // 复制最低价数据
    if (CopyLow(symbol, timeframe, 0, periods, lows) < periods)
    {
        Print("Failed to copy low prices for ", symbol);
        return -1; // 返回错误值
    }
    
    // 计算振幅总和
    double sumAmplitude = 0.0;
    for (int i = 0; i < periods; i++)
    {
        sumAmplitude += (highs[i] - lows[i]); // 每个周期的振幅
    }
    
    // 计算平均振幅
    double averageAmplitude = sumAmplitude / periods;
    return averageAmplitude;
}

/**
 * 获取指定货币对的分数
    symbols[i] = symbol;
  }
  return true;
}
/**
 * 打印指定货币对得分, 绝对值越大越推荐购买
 */
bool printCredits(string symbol, datetime begin_date, datetime end_date)
{
    int swap_mode;
    string str_swap_mode;
    Swapper::getSwapMode(symbol, swap_mode, str_swap_mode);
    double swap_usd = 0;
    // 一手多单隔一夜能得的利息
   double swap_long=SymbolInfoDouble(symbol,SYMBOL_SWAP_LONG);
    // 一手空单隔一夜能得的利息
   double swap_short=SymbolInfoDouble(symbol,SYMBOL_SWAP_SHORT);
  //  m_log.info("swap_long:" + swap_long + " " + "swap_short:" + swap_short);
   m_log.info(StringFormat("%s %s, swap_long: %f, swap_short: %f", symbol, str_swap_mode, swap_long, swap_short));
  int credits = getSymbolCredits(symbol, begin_date, end_date);
  if ((credits >= SHOW_CREDIT_MIN && swap_short > 0))
  {
    swap_usd = Swapper::pointToUsd(symbol, swap_short);
    m_log.info("**** " + "做空 " + symbol + ": [" + credits + "] swap: " + swap_usd + " ****");   
  } 
  else if (-1 * credits >= SHOW_CREDIT_MIN && swap_long > 0)
  {
    swap_usd = Swapper::pointToUsd(symbol, swap_long);
    m_log.info("**** " + "做多 " + symbol + ": [" + credits + "] swap: " + swap_usd + " ****");
  } 
  else if (MathAbs(credits) > 90) 
  {  
    
    if (credits < 0)
    { // 做多
      swap_usd = Swapper::pointToUsd(symbol, swap_long);
      m_log.info("做多 " + symbol + ": " + credits + " swap: " + swap_usd); 
    }
    else
    { // 做空
      swap_usd = Swapper::pointToUsd(symbol, swap_short);
      m_log.info("做空 " + symbol + ": " + credits + " swap: " + swap_usd); 
    }
       
  } else {
    m_log.info(symbol + ": " + credits);    
  }

  return true;
}

/**
 * 计算货币对在某时间范围内得分
 */
int getSymbolCredits(string symbol, datetime begin_date, datetime end_date)
{
  WavePoint *wave_point_ = new WavePoint(symbol, begin_date, end_date, PERIOD_D1);
  return wave_point_.getCredits();
}
bool printLevel(string symbol, datetime begin_date, datetime end_date)
{
  if (SymbolInfoInteger(symbol, SYMBOL_TRADE_MODE) == SYMBOL_TRADE_MODE_DISABLED)
  {
    m_log.debug(symbol + "%s 不能正常交易, 跳过");
    return false;
  }
  char price_level = getSymbolLevel(symbol, begin_date, end_date);
  
  if (price_level == 1) {
    Print("****");
    m_log.info(symbol + "波段位置: 上升初期, 可买入");
    return true;
  } 
  if (price_level == 4){      
    Print("****");
    m_log.info(symbol + "波段位置: 上升末期, 可卖出");
    return true;
  }
  if (price_level == -1) {
    Print("****");
    m_log.info(symbol + "波段位置: 下降初期, 可卖出");
    return true;
  } 
  if (price_level == -4){      
    Print("****");
    m_log.info(symbol + "波段位置: 下降末期, 可买入");
    return true;
  }
  return true;
}
/**
 * 判断是否为有效货币对
 */
bool isForexSymbol(string symbol)
{
  // 获取交易商品类型
  int symbolType=SymbolInfoInteger(symbol,SYMBOL_TRADE_CALC_MODE);
  bool is_forex = false;
  if(symbolType==SYMBOL_CALC_MODE_FOREX) is_forex = true;
  return is_forex;
  
  // if (symbol_size == 6)
  // {
  //   if (!StringToUpper(symbol))
  //   {
  //     if (is_forex)
  //       m_log.error("手工判断错误");
  //     return false;
  //   }
  //   for (int i=0; i < symbol_size; ++i)
  //   {
  //     ushort fchar = StringGetCharacter(symbol, i);
  //     if (fchar < 'A' || fchar > 'Z')
  //     {
  //       if (is_forex)
  //         m_log.error("手工判断错误");
  //       return false;
  //     }
  //   }
  // } else {
  //   if (is_forex)
  //     m_log.error("手工判断错误");
  //   return false;
  // }
}

/**
 * 判断某货币对在某时间范围内处于何种位置
 */
char getSymbolLevel(string symbol, datetime begin_date, datetime end_date)
{
  WavePoint *wave_point_ = new WavePoint(symbol, begin_date, end_date, PERIOD_D1);
  char price_level = wave_point_.getPosition();
  return price_level;
}

// void validSymbols(long max_spread, string &symbols[])
// {
//   int mt_total = SymbolsTotal(false);
//   int mw_total = SymbolsTotal(true);
//   printf("平台支持的所有货币对数量: %d, MW中显示的货币对数量: %d", mt_total, mw_total);
//     long spread = 0;
//     string symbol; 
//     // string fix_float = "";
    
//     // 在Market Watch中显示
//     bool show_in_mw = true;

//     for (int i=mt_total-1; i>=0; --i)
//     {
//       symbol = SymbolName(i, false); // 从平台列表中取一个symbol
//       show_in_mw = true;
      
//       if (SymbolInfoInteger(symbol, SYMBOL_TRADE_MODE) == SYMBOL_TRADE_MODE_DISABLED)
//       {
//         m_log.debug(symbol + " 不能正常交易, 跳过");
//         // SymbolSelect(symbol, false);
//         continue;
//       }

//       bool is_fx_symbol = isForexSymbol(symbol);
//       if (is_fx_symbol)
//       { // 如果是货币对
//         spread = validSpread(symbol);
//           if (spread > i_mw_max_spread)
//           {
//             m_log.info(StringFormat("%s 点差为%d超出指定值, 跳过", symbol, spread));
//             show_in_mw = false;
//           }

//       } else { // 如果不是货币对
//         if (i_forex_only)
//         {
//           show_in_mw = false;
//           m_log.debug(StringFormat("%s 不是严格的货币对, 不会在Market Watch中显示", symbol));
//         }
//       }

//       SymbolSelect(symbol, show_in_mw);
//       // fix_float = SymbolInfoInteger(symbol, SYMBOL_SPREAD_FLOAT) ? "浮动" : "固定";

//   if (i_mw_tidy_flag)
//   {
//     }

//   }

// }

/**
 * 取指定货币对的点差值, 成功返回点差(点数), 失败返回0
 * @param symbol 指定的货币对
 */
long validSpread(string symbol)
{
  // 点差类型(浮动,固定)
  bool is_spread_float = SymbolInfoInteger(symbol, SYMBOL_SPREAD_FLOAT);
  long spread = 0;
  if (!SymbolInfoInteger(symbol, SYMBOL_SPREAD, spread))
  {
    m_log.debug(StringFormat("函数获取%s点差失败, 错误号: %d", symbol, GetLastError()));
    
  }
  if (0 == spread)
  {
    double ask=SymbolInfoDouble(symbol, SYMBOL_ASK); 
    double bid=SymbolInfoDouble(symbol, SYMBOL_BID); 
    double spread_value=ask-bid; 
    spread=MathRound(spread_value/SymbolInfoDouble(symbol,SYMBOL_POINT)); 
  }
  m_log.info(StringFormat("%s %s spread:%d", symbol,(is_spread_float ? "float" : "fix"), spread));  
    
  return spread;
}
/**
 * 初始化MW
 */
void initMW()
{
  int mt_total = SymbolsTotal(false);
  int mw_total = SymbolsTotal(true);
  printf("平台支持的所有货币对数量: %d, MW中显示的交易对象数量: %d", mt_total, mw_total);
  if (i_mw_tidy_flag)
  {
    long spread = 0;
    string symbol; 
    // string fix_float = "";
    for(int i=0; i<mt_total-1; i++)
    {
      symbol = SymbolName(i, false); // 从平台列表中取一个symbol
      // 判断一个交易对象是否为外汇需要先加入MW
      SymbolSelect(symbol, true); // 加入MW
      if (!isForexSymbol(symbol))
      {
        SymbolSelect(symbol, false); // 从MW删除
      }
    }

    
    // 在Market Watch中显示
    // bool show_in_mw = true;
    // string symbols[];
    // filterSymbols(false, i_forex_only, i_min_price_spread, symbols);
    // int symbols_count = ArraySize(symbols);
    // for (int i = 0; i < symbols_count; i++)
    // {
    //   SymbolSelect(symbols[i], true);
    // }
    // bool found = false;
    // int mw_total = SymbolsTotal(true);

    // for(int i=mw_total-1; i>=0; --i)
    // {
    //   symbol = SymbolName(i,true);
    //   for(int j=0;j<symbols_count-1;j++)
    //   {
    //     if(symbol == symbols[j])
    //     {
    //       found = true;
    //       break; 
    //     }
    //   }
    //   if(found)
    //   {// 找到了
    //     found = false;
    //     continue;
    //   } 
    //   else 
    //   { // 没找到
    //     SymbolSelect(symbol, false); // 从MW中删除
    //   }
    // }
    // for (int i=mt_total-1; i>=0; --i)
    // {
    //   symbol = SymbolName(i, false); // 从平台列表中取一个symbol
    //   show_in_mw = true;
      
    //   if (SymbolInfoInteger(symbol, SYMBOL_TRADE_MODE) == SYMBOL_TRADE_MODE_DISABLED)
    //   {
    //     m_log.debug(symbol + " 不能正常交易, 跳过");
    //     SymbolSelect(symbol, false);
    //     continue;
    //   }

    //   bool is_fx_symbol = isForexSymbol(symbol);
    //   if (is_fx_symbol)
    //   { // 如果是货币对
    //     if (SymbolInfoInteger(symbol, SYMBOL_SPREAD, spread))
    //     {
    //       if (spread > i_mw_max_spread)
    //       {
    //         m_log.info(StringFormat("%s 点差为%d超出指定值, 不会在Market Watch中显示", symbol, spread));
    //         show_in_mw = false;
    //       }
          
    //     } else {
    //       m_log.error(StringFormat("获取%s点差失败", symbol));
    //     }

    //   } else { // 如果不是货币对
    //     if (i_forex_only)
    //     {
    //       show_in_mw = false;
    //       m_log.debug(StringFormat("%s 不是严格的货币对, 不会在Market Watch中显示", symbol));
    //     }
    //   }

    //   SymbolSelect(symbol, show_in_mw);
    //   // fix_float = SymbolInfoInteger(symbol, SYMBOL_SPREAD_FLOAT) ? "浮动" : "固定";

    // }

  }

}